'''
Created on 22/10/2009

@author: eh01
'''
from datastore.resource import Resource
from runtime.task import Task
import datetime
import threading
import time
import logging

class TimeTask(Task):
    '''
    Task to assist with timer and cron activities. A TimeTask client will have to 'put' to either the 
    'timer' or 'cron' uri to create a timer or cron job. The resource will contain the interval at which the
    client will want to be notified. The job will run as a seperate thread and 'put' to the time job uri to 
    notify the client, so the client will need to watch/process this uri. Once the client no longer requires
    the time job, the client can delete it by deleting the resource at the time job uri.
    '''
    
    def __init__(self, ds, spaces, task_uri, task_def_rs):
        # <purpose>
        #
        # Parameters:
        #     ds : datastore.database.DataStore
        #     spaces : runtime.spaces.Spaces - Reference to spaces
        #     task_uri : String - the uri for this task. The root uri will most
        #                likely be the 'binding' uri for this task.
        #     task_def_rs : datastore.resource.Resource - Task definition resource.
        #
        
        Task.__init__(self, ds, spaces, task_uri, task_def_rs)
        
        self.logger = logging.getLogger('yarra.services.timetask.TimeTask')
        self.logger.log(logging.DEBUG, "ds (%s), spaces (%s), task_uri (%s), task_def_rs (%s)" % (ds, spaces, task_uri, task_def_rs.pretty_print()))
    
    def cb_start(self, method, uri, res, result, spaces_context, **kwargs):
        uri_time = spaces_context.uri('time:')
        spaces_context.process(uri_time.timestamp, get='cb_timestamp')
        spaces_context.watch_all(['put'], uri_time.timer.collection(True), 'cb_create_timer')
        spaces_context.watch_all(['delete'], uri_time.timer.collection(True), 'cb_delete_time_job')
        spaces_context.watch_all(['put'], uri_time.cron.collection(True), 'cb_create_cron')
        spaces_context.watch_all(['delete'], uri_time.cron.collection(True), 'cb_delete_time_job')
        
        # a dictionary of all the current running time jobs.
        self.jobs = {}
    
    def cb_timestamp(self, method, uri, res, result, spaces_context, **kwargs):
        '''
        Returns the a timestamp of the current time back to the user.
        '''
        date_time_now = datetime.datetime.now()
        rs = Resource()
        rs.timestamp = date_time_now
        return (True, rs)
    
    def cb_create_timer(self, method, uri, new_uri, result, spaces_context, **kwargs):
        '''
        Creates a TimeJob that will put to the timer uri at the interval specified. Users will have to 
        watch/process that uri for the 'put' and then delete the timer.
        '''
        uri_time = spaces_context.uri('time:')
        if uri != uri_time.timer.collection(True):
            return
        res = spaces_context.get(new_uri)        
        interval = res.interval
        time_job = self.create_time_job(interval, spaces_context, new_uri, False)
        time_job.start()
    
    def cb_create_cron(self, method, uri, new_uri, result, spaces_context, **kwargs):
        '''
        Creates a TimeJob that will put to the timer uri at the interval specified. Users will have to 
        watch/process that uri for the 'put's and then delete the timer.
        '''
        uri_time = spaces_context.uri('time:')
        if uri != uri_time.cron.collection(True):
            return
        res = spaces_context.get(new_uri)
        interval = res.interval
        time_job = self.create_time_job(interval, spaces_context, new_uri, True)
        time_job.start()
    
    def create_time_job(self, interval, spaces_context, time_job_uri, is_cron):
        '''
        Creates a TimeJob with the specified interval and adds it to the jobs dictionary.
        '''
        time_job = TimeJob(interval, spaces_context, is_cron, time_job_uri)
        self.jobs[time_job_uri] = time_job 
        return time_job
    
    def cb_delete_time_job(self, method, uri, res, result, spaces_context, **kwargs):
        '''
        Stops and deletes the time job from the job dictionary.
        '''
        time_job = self.jobs[uri]
        time_job.stop_job()
        del self.jobs[uri]
    
class TimeJob(threading.Thread):
    '''
    TimeJob is a thread which represents either a timer or cron job. At the interval specified, it 'put's to 
    the time_job_uri. If it is a timer job, it will 'put' to the time_job_uri.pop only once then the thread will finish,
    otherwise if it is a cron job, it will sleep for the specified interval and then 'put' to the time_job_uri.pop again.
    The cron job must be stopped by the TimeTask.
    '''
    def __init__(self, interval, spaces_context, cron, time_job_uri):
        threading.Thread.__init__(self)
        self.interval = interval
        self.spaces_context = spaces_context
        # indicates if it is a cron or timer job.
        self.cron = cron
        self.stop = False
        self.loop_counter = 0
        # uri which the TimeJob will 'put' to
        self.time_job_uri = time_job_uri
        
    def run(self):
        # sleep for the interval then wake up and start the loop
        time.sleep(self.interval)       
        # loop if its not stop and if it is either a cron job or a timer task and the run count is Zero.
        while ((self.loop_counter == 0 and not self.cron) or self.cron) and not self.stop:
            res = Resource()        
            self.spaces_context.put(self.time_job_uri.pop, res)
            # increment the loop_counter
            self.loop_counter += 1
            # sleep for the interval then wake up and put to the time_job_uri
            time.sleep(self.interval)
            
    def stop_job(self):
        '''
        Stop job by setting the stop flag to break the loop on next iteration.
        '''
        self.stop = True